#include "EPollPoller.h"
#include "Logger.h"
#include "Channel.h"

#include <errno.h>
#include <unistd.h>
#include <strings.h>

// 定义 Channel 的状态
const int kNew = -1;  // Channel 新创建，未加入 epoll 监听
const int kAdded = 1; // Channel 已加入 epoll 监听
const int kDeleted = 2; // Channel 从 epoll 监听中删除

// 构造函数
EPollPoller::EPollPoller(EventLoop *loop)
    : Poller(loop),  // 初始化基类 Poller
      epollfd_(::epoll_create1(EPOLL_CLOEXEC)),  // 创建 epoll 文件描述符，使用 EPOLL_CLOEXEC 标志
      events_(kInitEventListSize)  // 初始化事件列表容量
{
    // 检查 epoll 文件描述符创建是否成功
    if (epollfd_ < 0)
    {
        LOG_FATAL("epoll_create error:%d \n", errno);  // 创建失败，记录致命错误日志
    }
}

// 析构函数
EPollPoller::~EPollPoller() 
{
    ::close(epollfd_);  // 关闭 epoll 文件描述符
}

Timestamp EPollPoller::poll(int timeoutMs, ChannelList *activeChannels)
{
    // 实际上应该用LOG_DEBUG输出日志更为合理 (因为这个函数会经常被调用)
    LOG_INFO("func=%s => fd total count:%lu \n", __FUNCTION__, channels_.size());

    // &*events_.begin(): vector底层数组的起始地址; events_.begin()首元素的迭代器, 解引用变成值, 然后取地址;
    int numEvents = ::epoll_wait(epollfd_, &*events_.begin(), static_cast<int>(events_.size()), timeoutMs);

    // 首先保存当前errno的值，以便后续恢复。这样做是为了在处理完epoll_wait()返回的值后，能够保留之前的errno，
    // 以避免误导性地将 EINTR 错误处理成了其他错误。
    int saveErrno = errno;  // 保存 error 以便恢复
    Timestamp now(Timestamp::now()); // 获取当前时间

    // 根据 epoll_wait 返回的结果处理事件
    if (numEvents > 0)
    {
        LOG_INFO("%d events happened \n", numEvents);  // 记录发生的事件数量
        fillActiveChannels(numEvents, activeChannels); // 填充活跃的通道列表
        if (numEvents == events_.size())
        {
            events_.resize(events_.size() * 2); // 如果事件数组已满, 扩大容量
        }
    }
    else if (numEvents == 0)
    {
        // 编译时 g++ -DMUDEBUG your_code.cpp -o your_executable 或者 在代码中 #define MUDEBUG
        // 才会去运行 LOG_DEBUG 宏!!!!!!
        LOG_DEBUG("%s timeout! \n", __FUNCTION__);  // 事件等待超时
    } 
    else
    {
        // epoll_wait() 在等待事件时可能会被信号中断（EINTR），但是这种中断并不是错误，因此需要特殊处理
        // 首先检查保存的errno是否为EINTR，如果不是，说明是其他错误（如EBADF、EINVAL等），则在日志中记录该错误。
        if (saveErrno != EINTR)
        {
            errno = saveErrno;
            LOG_ERROR("EPollPoller::poll() err!");  // 处理错误, 非 EINTR 错误
        }
    }
    return now;  // 返回当前时间
}

// channel update remove => EventLoop updateChannel removeChannel => Poller updateChannel removeChannel
/**
 *              EventLoop  =>   poller.poll
 *     ChannelList      Poller
 *                     ChannelMap  <fd, channel*>   epollfd
 */ 
// 更新 Channel 的状态
void EPollPoller::updateChannel(Channel *channel)
{
    const int index = channel->index();  // 获取 Channel 当前状态
    LOG_INFO("func=%s => fd=%d events=%d index=%d \n", __FUNCTION__, channel->fd(), channel->events(), index);

    if (index == kNew || index == kDeleted)  // 如果是新建或已删除状态
    {
        if (index == kNew)
        {
            int fd = channel->fd();  
            channels_[fd] = channel; // 添加 channels 映射
        }

        channel->set_index(kAdded); // 更新状态为已添加
        update(EPOLL_CTL_ADD, channel); // 调用 epoll_ctl 添加监听
    }
    else  // channel已经在poller上注册过了
    {
        int fd = channel->fd();
        if (channel->isNoneEvent())
        {
            update(EPOLL_CTL_DEL, channel); // 如果没有事件, 调用 epoll_ctl 删除监听
            channel->set_index(kDeleted); // 更新状态为已删除
        }
        else
        {
            update(EPOLL_CTL_MOD, channel); // 更新监听的事件
        }
    }
}

// 从poller中删除channel
void EPollPoller::removeChannel(Channel *channel) 
{
    int fd = channel->fd(); 
    channels_.erase(fd); 

    LOG_INFO("func=%s => fd=%d\n", __FUNCTION__, fd); 
    
    int index = channel->index();
    if (index == kAdded) // 如果当前状态是已添加
    {
        update(EPOLL_CTL_DEL, channel);  // 调用 epoll_ctl 删除监听
    }
    channel->set_index(kNew); // 重置状态为新建
}

// 填写活跃的连接
void EPollPoller::fillActiveChannels(int numEvents, ChannelList *activeChannels) const
{
    for (int i=0; i < numEvents; ++i)
    {
        Channel *channel = static_cast<Channel*>(events_[i].data.ptr);
        channel->set_revents(events_[i].events);
        activeChannels->push_back(channel); // EventLoop就拿到了它的poller给它返回的所有发生事件的channel列表了
    }
}

// 更新channel通道 epoll_ctl add/mod/del
void EPollPoller::update(int operation, Channel *channel)
{
    struct epoll_event event;  // C++ 11 可以把 struct 去掉 --> epoll_event event; 是OK的!
    bzero(&event, sizeof(struct epoll_event)); 
    
    int fd = channel->fd();

    event.events = channel->events();
    event.data.fd = fd; 
    // event.data.ptr = channel; 是为了在处理 epoll 事件时能够直接从 epoll 事件结构体 epoll_event 中获取到
    //                           对应的 Channel 对象。这种设计方式提高了代码的效率和可读性; 
    event.data.ptr = channel;   
    
    if (::epoll_ctl(epollfd_, operation, fd, &event) < 0)  // 执行 epoll_ctl 操作
    {
        if (operation == EPOLL_CTL_DEL)
        {
            LOG_ERROR("epoll_ctl del error:%d\n", errno);
        }
        else
        {
            LOG_FATAL("epoll_ctl add/mod error:%d\n", errno);
        }
    }
}
